/*
 * Copyright (c) 2010 Zauber S.A.  -- All rights reserved
 */
package ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.bitwise;

import java.util.LinkedList;
import java.util.Queue;

import ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.Expresion;
import ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.impl.ExpresionPrimitive;


/**
 * Clase de utilidad
 * 
 * @author Juan F. Codagnone
 * @since Mar 27, 2010
 */
public final class BitwiseGenUtils {
    
    /** constructor */
    private BitwiseGenUtils() {
        // utility class
    }
    
    /** primitivas del lenguaje */
    public static final ExpresionPrimitive[] PRIMITIVES = {
        new AndExpresionPrimitive(),
        new OrExpresionPrimitive(),
        new NandExpresionPrimitive(),
        new NorExpresionPrimitive(),
        new VariableExpresionPrimitive(6), 
//        new NotExpresionPrimitive(), 
//        new XorExpresionPrimitive(),
    };

    
    /**  
     * Calcula la distancia de hamming:
     *   http://en.wikipedia.org/wiki/Hamming_distance 
     */
    public static int hammingDistance(final boolean []a, final boolean []b) {
        assert a != null;
        assert b != null;
        assert a.length == b.length;
        int ret = 0;
        for(int i = 0; i < a.length; i++) {
            ret += (a[i] ^ b[i]) ? 1 : 0;
        }
        return ret;
    }
    
    /** Calcula la cantidad de respuestas correctas */
    public static int respuestasCorrectas(final Boolean []a, final boolean []b) {
        assert a != null;
        assert b != null;
        assert a.length == b.length;
        int ret = 0;
        for(int i = 0; i < a.length; i++) {
            if(a[i]  == null) {
                ret++;
            }  else {
                ret += (a[i] ^ b[i]) ? 0 : 1;
            }
        }
        return ret;
    }
    
    /** convierte un arreglo de booleans en 101110001 */
    public static String toString(final boolean []a) {
        final StringBuilder sb = new StringBuilder();
        for(final boolean b : a) {
            sb.append(b ? '1' : '0');
        }
        return sb.toString();
    }
    
    /** convierte un arreglo de booleans en 101110001 */
    public static String toString(final Boolean []a) {
        final StringBuilder sb = new StringBuilder();
        for(final Boolean b : a) {
            sb.append(b == null ? "X" : b ? '1' : '0');
        }
        return sb.toString();
    }
    

    /**
     * Parsea un arbol de expresiones que fue guardado serializado de forma 
     * pre-order.
     *  
     * @param s  arbol en formato caracter
     * @param primitives primitivas del lenguaje de expresion
     * @param variables  terminales
     */
    public static Expresion  parseExpresion(
            final String s, 
            final ExpresionPrimitive []primitives,
            final Variable[]variables) {
        final Queue<Character> q = new LinkedList<Character>();
        for(final char c : s.toCharArray()) {
            q.add(c);
        }
        return parseExpresion(q, primitives, variables);
    }
    
    /** @see #parseExpresion(String, ExpresionPrimitive[], Variable[]) */
    protected static Expresion  parseExpresion(final Queue<Character> q, 
                             final ExpresionPrimitive []primitives,
                             final Variable[]variables) {
        final char c = q.poll();
        final ExpresionPrimitive primitive = find(c, primitives);
        final Expresion ret;
        if(primitive.isTerminal()) {
            int i = 0;
            for(; i < Variable.ALPHABET.length; i++) {
                if(Variable.ALPHABET[i] == c) {
                    break;
                }
            }
            ret = variables[i];
        } else {
            final Expresion []args = new Expresion[primitive.getSubExpresions()];
            for(int i = 0; i < args.length; i++) {
                args[i] = parseExpresion(q, primitives, variables);
            }
            ret = primitive.create(args); 
        }
        
        return ret;
    }
    
    /** encuentra la primitiva para cierto caracter */
    public static ExpresionPrimitive find(final char c, 
            final ExpresionPrimitive []primitives) {
        for(final ExpresionPrimitive primitive : primitives) {
            if(primitive.canCreate(c)) {
                return primitive;
            }
        }
        throw new IllegalStateException("Can't find primitive for char: " + c);
    }
    
    /** 
     * convierte una secuencia de bits a long interprentandolo como 
     * potencias de 2 
     */
    public static long toLong(final Boolean []bits, final int n) {
        int nn = Math.min(bits.length, n);
        
        long i = 0;
        for(int j = 0; j < nn; j++) {
            i  = i << 1;
            i = i | (bits[j] ? 1 : 0);
        }
        
        return i;
    }
    
    /** 
     * convierte una secuencia de bits a long interprentandolo como 
     * potencias de 2 
     */
    public static long toLong(final boolean []bits, final int n) {
        int nn = Math.min(bits.length, n);
        
        long i = 0;
        for(int j = 0; j < nn; j++) {
            i  = i << 1;
            i = i | (bits[j] ? 1 : 0);
        }
        
        return i;
    }
    
    /** @see #toLong(boolean[], int))*/
    public static long toLong(final boolean []bits) {
        return toLong(bits, bits.length);
    }
    
    /** @see #toLong(boolean[], int))*/
    public static long toLong(final Boolean []bits) {
        return toLong(bits, bits.length);
    }
}
